﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Threading;

// MEF .
using System.ComponentModel.Composition.Primitives;


namespace DailyFx.UI 
{

    public abstract class Bootstrapper : IServiceLocator
	{
		/// <summary>
		///		indicates if this bootstrapper is already initialised .
		/// </summary>
        private bool _isBooted;

       
        /// <summary>
        ///		c;tor .
        /// </summary>
        protected Bootstrapper ( bool useApplication = true)
		{
			Boot();
        }


        /// <summary>
        ///		Boots up this framework.
        /// </summary>
        public void Boot ()
		{
            if( _isBooted ) 
                return;
            
			this._isBooted = true;

            if( Designer.IfTrue ) 
			{
                try 
				{
					BootIfDesigner ();
                }
				catch 
				{
                    //if something fails at design-time, there's really nothing we can do...
                    _isBooted = false;
                    throw;
                }
            }
            else
			{
				BootIfApplication ();
            }
        }

        /// <summary>
        ///		Called by the bootstrapper's constructor at design time to start the framework.
        /// </summary>
        protected virtual void BootIfDesigner ()
		{
            IoC.DoGet = GetService;
            IoC.DoGetAll = GetServices;
            IoC.Satisfy = SatisfyInstance;
        }

        /// <summary>
        ///		Called by the bootstrapper's constructor at runtime to start the framework.
        /// </summary>
        protected virtual void BootIfApplication ( ) 
		{
            Execution.SetUIThreadDirector ( true, null );

            Mediator.DefaultBroadcastingThreadMarshaller = Execution.OnUIThread;
            Mediator.HandlerTaskProcessing = (target, call) => 
			{
#if !SILVERLIGHT || SL5 || WP8
                var c = call as ICommand;
                if ( c != null) 
				{
                    call = new ICommand[] { c };
                }
#endif

                var calls = call as IEnumerable<ICommand>;
                if ( calls != null ) 
				{
                    var viewAware = target as IViewAware;
                    var view = viewAware != null ? viewAware.GetView() : null;
                    var context = new WeakAction { Target = target, View = (DependencyObject)view };

                    CoroutineSystem.Call ( calls.GetEnumerator(), context );
                }
            };

			Application.Current.Startup += OnStartup;
#if SILVERLIGHT
            Application.Current.UnhandledException += OnUnhandledException;
#else
			Application.Current.DispatcherUnhandledException += OnUnhandledException;
#endif
			Application.Current.Exit += OnExit;


			IoC.DoGet = GetService ;
            IoC.DoGetAll = GetServices ;
			IoC.Satisfy = SatisfyInstance ;
        }



        /// <summary>
        ///		Override this to provide an IoC specific implementation.
        /// </summary>
        public virtual object GetService ( Type service, string key ) 
		{
#if NET
            if ( service == typeof(IWindowManager) )
                service = typeof(WindowManager);
#endif

            return Activator.CreateInstance( service );
        }
		//
		object IServiceProvider.GetService ( Type service )
		{
			return this.GetService( service, null );
		}

        /// <summary>
        ///		Override this to provide an IoC specific implementation
        /// </summary>
        public virtual IEnumerable<object> GetServices ( Type service )
		{
            return new[] { Activator.CreateInstance(service) };
        }

        /// <summary>
        ///		Override this to provide an IoC specific implementation.
        /// </summary>
        protected virtual void SatisfyInstance ( object instance ) 
		{ 
		}

        /// <summary>
        ///		Override this to add custom behavior to execute after the application starts.
        /// </summary>
        protected virtual void OnStartup ( object sender, StartupEventArgs e ) 
		{
			var startupFuncs = this.GetServices( typeof(StartupFunction) ).Cast<ExportedDelegate>()
									.Select( x => (StartupFunction)x.CreateDelegate(typeof(StartupFunction)) );

			startupFuncs.Apply( s => s() );

			// Locates and shows the main window .
			//
#if SILVERLIGHT && !WINDOWS_PHONE

			var viewModel = IoC.DoGet<>( typeof(IMainWindow), null );
            var view = View.CreateOrRetrieveViewElement ( viewModel, null, null );
            Bind.SetBinding (viewModel, view, null);

            var uistatus = viewModel as IUIStatus ;
            if( uistatus != null )
                activator.SetEnabled ();

            // Mouse.Init ( view );
            Application.RootVisual = view;
#elif NET
			var windowManager = IoC.Get<IWindowManager>();
			windowManager.ShowWindow( IoC.DoGet( typeof(IWindow), null), null, null );
#endif
		}

        /// <summary>
        ///		Override this to add custom behavior on exit.
        /// </summary>
        protected virtual void OnExit(object sender, EventArgs e)
		{
		}


        /// <summary>
        ///		Override this to add custom behavior for unhandled exceptions.
        /// </summary>
#if SILVERLIGHT
        protected virtual void OnUnhandledException ( object sender, ApplicationUnhandledExceptionEventArgs e )
#else	
		 protected virtual void OnUnhandledException ( object sender, DispatcherUnhandledExceptionEventArgs e ) 

#endif
		{ 
		}

            
    }

}
